/// 历史保留者[HistoryKeeper]
///
/// 1. [GearTimerTimer]需要获取一段delay之前的值，最好的方案就是用它来保存历史数据
///
/// `方法 method`
///
/// 1. 获取历史数据[fetch]
/// 2. 保存数据[save]
abstract class HistoryKeeper<T> {
  /*——————————————*/

  /// 获取历史数据
  ///
  /// 由于数据是散点式分布的，所以不太可能获得刚好的数据，而是取距离最近的
  ///
  /// `输入 input`
  ///
  /// 1. 距离时间[distance]
  ///   - 指的是要多久之前的数据
  ///   - 必须大于等于0，否则无意义
  T? fetch([Duration distance = Duration.zero]);

  /// 保存数据
  ///
  /// `输入 input`
  ///
  /// 1. 值[value]
  void save(T value) {}

  /*——————————————*/

  /// `标准构建方式`
  ///
  /// 1. 通常在需要高速存储的时候使用（比如[GearTimer]存储之前的状态）
  /// 2. 如果需要存储长期的记录，也可以考虑用用看
  /// 3. 通常不对外暴露，而是由其他class使用
  ///
  /// `用例 case`
  ///
  /// ```
  /// final _keeper = HistoryKeeper();
  /// ```
  factory HistoryKeeper() => _HistoryKeeperImplement<T>();
}

/// 实现：历史保留者[HistoryKeeper]
///
/// 1. 它只保留特定时间段内的数据，超过这个时间段的就会丢弃
/// 2. 每次有人向它要之前的数据时，它会做这几件事：
///   - 如果这段时间太长，它会刷新“特定时间”
///   - 清除过期的数据
///   - 返回距离这个时间最近的记录
/// 3. 任意时刻都可以往里面存储数据（数据会按时间先后顺序排列好，所以每次清除过期数据的时候，会从最前面开始清理）
/// 4. 建议在高频读写的场景使用（比如毫秒为单位），而不要用来保存长期数据（比如天为单位），此条待定
class _HistoryKeeperImplement<T> implements HistoryKeeper<T> {
  final List<HistoryRecord<T>> history = [];

  static const int capacity = 100;
  bool get overflow => history.length > capacity;

  var maxDistance = Duration.zero;
  bool isExpired(DateTime time) {
    // 查看距离现在的时间
    final actualDistance = DateTime.now().difference(time);
    // 如果超过了最大期限，那么就过期了
    return actualDistance > maxDistance;
  }

  @override
  fetch([Duration distance = Duration.zero]) {
    // 输入不能为负，否则没意义
    assert(distance >= Duration.zero);
    // 如果distance是有史以来最长的，那么将它记录下来
    if (distance > maxDistance) maxDistance = distance;

    // 如果没有数据，返回null
    if (history.isEmpty) return null;
    // 如果有数据，那么计算distance所在的时间点
    final target = DateTime.now().subtract(distance);
    // 找到距离distance最短的记录
    HistoryRecord closest = history.first;
    for (final record in history) {
      final delta = record.key.difference(target).abs();
      final closestDelta = closest.key.difference(target).abs();

      if (delta < closestDelta) closest = record;
    }
    // 返回数据
    return closest.value;
  }

  @override
  void save(T value) {
    // 将数据打上时间戳后，存入历史
    final record = HistoryRecord<T>(DateTime.now(), value);
    history.add(record);
    // 如果历史超过容量，那么开始清扫
    if (overflow) clean();
  }

  void clean() {
    // 当记录大于容量，并且最久远的记录已过期时
    final furtherest = history.first.key;
    if (overflow && isExpired(furtherest)) {
      // 清除最久远的哪个
      history.removeAt(0);
      // 再次检查
      clean();
    }
  }
}

typedef HistoryRecord<T> = MapEntry<DateTime, T>;
